# -*- coding: utf-8 -*-
# created on 2016/5/5
from itertools import combinations
from sympy import (fraction, Poly, Abs, Rational, exp, log, cancel, degree, Mul, Pow, simplify, S, EmptySet, Add,
                   Interval, Union, sympify, Sum)
from mathsolver.functions.base import BaseEq, BaseEqs, BaseSymbolValue, BasePieceFunc
from mathsolver.functions.hanshu.helper import check_inter
from mathsolver.functions.root.jiefangchen import JieFangChen
from mathsolver.functions.root.jiefangchenzu import JieFangChenZu
from mathsolver.functions.sympy_utils import default_symbol


# is 1/x
def is_invrat_func(f, sym):
    return f.is_Pow and f.args[1] == -1 and f.args[0] == sym


# is sqrt(x)
def is_sqrt_func(f, sym):
    return f.is_Pow and f.args[1] == Rational(1, 2) and f.args[0] == sym


# is x**a  这个包含上两个,即 a=-1 和 a=1/2 的情况.
def is_mihanshu(f, sym):
    return f.is_Pow and f.args[0] == sym and (sym not in f.args[1].free_symbols)


# is exp(x)
def is_exp_func(f, sym):
    return f.func == exp and f.args[0] == sym


# is a ** x
def is_zhishuhanshu(f, sym):
    return is_exp_func(f, sym) or (f.is_Pow and (f.args[1] == sym or f.args[1] == -sym))


# is log(x,a)
def is_duishuhanshu(f, sym):
    if f.is_Mul:
        a, f = f.as_independent(sym)
    return isinstance(f, log) and len(f.args[0].free_symbols) > 0


# is Abs(x)
def is_abs_func(f, sym):
    return isinstance(f, Abs) and f.args[0] == sym


def is_interval(arg):
    """判断 arg 是否是区间"""
    return isinstance(arg, (Interval, Union))


# 是否分式函数 只考虑一整个分式函数,优先考虑优先解决.
def is_frac_func(f):
    f = cancel(f)
    u, d = fraction(f)
    return u.is_polynomial() and d.is_polynomial() and len(u.free_symbols) > 0 and len(d.free_symbols) > 0


# y = a**(x**2+x+1)
def is_fuhe_zhishuhanshu(f):
    """
    是否为复合型的幂指数函数
    :param f: 函数表达式
    :return: True or False
    """
    return f.is_Pow and len(f.args[1].free_symbols) > 0


def is_piecefunc(func):
    """是否是分段函数"""
    return isinstance(func, BasePieceFunc)


def is_absfunc(func):
    """是否是绝对值函数"""
    return "Abs(" in str(func.expression)


def is_sumabsfunc(func):
    """是否是绝对值相加函数"""
    expr = func.expression
    return expr.func == Sum and expr.args[0].func == Abs


def is_constantfunc(func):
    """是否是常数函数"""
    if is_piecefunc(func):
        return False
    expr, var = func.expression, func.var
    return var not in expr.free_symbols


# 判断:y=a*x+b or y=a*x
def yici_judge(*args):
    if len(args) == 2:
        f, symbol = args
    elif len(args) == 1:
        f = args[0]
        symbol = default_symbol(f)
    else:
        raise Exception("to do")
    try:
        return f.is_polynomial(symbol) and degree(f, gen=symbol) == 1
    except Exception:
        return False


# 判断反比例函数： k/x
def is_fanbili(f, symbol):
    return f.as_independent(symbol)[1] == sympify('1/x')


# 判断:y=a*x^2+b*x+c
def erci_judge(*args):
    if len(args) == 2:
        f, symbol = args
    elif len(args) == 1:
        f = args[0]
        symbol = default_symbol(f)
    else:
        raise Exception("to do")
    try:
        return f.is_polynomial(symbol) and degree(f, gen=symbol) == 2
    except Exception:
        return False


def sanci_judge(*args):
    if len(args) == 2:
        f, symbol = args
    elif len(args) == 1:
        f = args[0]
        symbol = default_symbol(f)
    else:
        raise Exception("to do")
    try:
        return f.is_polynomial(symbol) and degree(f, gen=symbol) == 3
    except Exception:
        return False


# 判断分式函数
def is_fenshi(f):
    f = cancel(f)
    u, d = fraction(f)
    return u.is_polynomial() and d.is_polynomial() and d.free_symbols


# 判断分式函数：y=a*x+b/c*x+d (c != 0)
def is_fenshi001(f, symbol):
    f = cancel(f)
    u, d = fraction(f)
    try:
        return (yici_judge(u, symbol) or not u.has(symbol)) and yici_judge(d, symbol)
    except Exception:
        pass


# 判断 log(fenshi001)
def is_logfenshi001(f, symbol):
    if f.has(log):
        args = f.args
        if len(args) == 2:
            f = args[1].args[0]
        else:
            f = args[0]
        return is_fenshi001(f, symbol)


# 判断分式函数：y=(a*x**2+b*x+c)/(d*x+e) (ad != 0)
def is_fenshi002(f, symbol):
    f = cancel(f)
    u, d = fraction(f)
    return erci_judge(u, symbol) and yici_judge(d, symbol)


# 判断分式函数：y=(a*x+b)/(c*x**2+d*x+e) (ac != 0)
def is_fenshi003(f, symbol):
    f = cancel(f)
    u, d = fraction(f)
    return erci_judge(d, symbol) and yici_judge(u, symbol)


# 判断分式函数：y=(a*x**2+b*x+c)/(d*x**2+e*x+f) (bd != 0)
def is_fenshi004(f, symbol):
    f = cancel(f)
    u, d = fraction(f)
    return erci_judge(d, symbol) and erci_judge(u, symbol)


# 判断根式类型：y=sqrt(f(x))
def is_sqrt(f):
    return f.is_Pow and f.args[1] == Rational(1, 2)


# 判断根式类型：y=sqrt(ax+b)+c (a != 0)
def is_genshi001(f, symbol):
    fei_genshi, genshi = f.as_independent(symbol)
    sqrt_part = genshi.as_independent(symbol)[1]
    return sqrt_part.is_Pow and sqrt_part.args[1] == Rational(1, 2) and yici_judge(sqrt_part.args[0], symbol) and (
        not fei_genshi.has(symbol))


# 判断根式类型：y=sqrt(ax+b) + cx+d (a != 0, c != 0)
def is_genshi002(f, symbol):
    if f.is_Add:
        parts = [part for part in f.args if part.has(symbol) and is_genshi001(part, symbol)]
        if len(parts) == 1:
            f_minus = f - parts[0]
            return f_minus.has(symbol) and yici_judge(f_minus, symbol)


# 判断根式类型: y=sqrt(1-x**2) + ax+b (a != 0)
def is_genshi003(f, symbol):
    if f.is_Add:
        parts = [part for part in f.args if "sqrt" in str(part)]
        if len(parts) == 1:
            genshi = parts[0]
            sqrt_inner = genshi.as_independent(symbol)[1].args[0]
            feigenshi = f - genshi
            if erci_judge(sqrt_inner, symbol) and yici_judge(feigenshi, symbol):
                a, b, c = Poly(sqrt_inner, symbol).all_coeffs()
                return a == -1 and b == 0 and c == 1


# 判断根式类型：y=sqrt(a+b*x) +/- sqrt(c+d*x)
def is_genshi004(f, symbol):
    if f.is_Add and len(f.args) == 2:
        return all(is_genshi001(part, symbol) for part in f.args)


# 判断根式类型：y=sqrt(a*x^2+b*x+c)+sqrt(m*x^2+n*x+p)
def genshi005_judge(f, symbol):
    if f.is_Add and len(f.args) == 2:
        parts = [part for part in f.args if part.has(symbol) and is_genshi006(part, symbol)]
        if len(parts) == 2:
            a1, b1, c1 = Poly(parts[0].args[0].as_expr(), symbol).all_coeffs()
            panbieshi1 = b1 ** 2 - 4 * a1 * c1
            a2, b2, c2 = Poly(parts[1].args[0].as_expr(), symbol).all_coeffs()
            panbieshi2 = b2 ** 2 - 4 * a2 * c2
            if a1 == a2:
                return (not panbieshi1.has(symbol)) and panbieshi1 <= 0 and (
                    not panbieshi2.has(symbol)) and panbieshi2 <= 0


# 判断根式类型：y=sqrt(a*x**2+b*x+c)+p
def is_genshi006(f, symbol):
    a, h = f.as_independent(symbol)
    return h.is_Pow and h.args[1] == Rational(1, 2) and erci_judge(h.args[0], symbol)


# 判断根式类型：y=sqrt(c-a*x**2+b*x)+m*x+n
def is_genshi007(f, symbol):
    if f.is_Add:
        parts = [part for part in f.args
                 if part.has(symbol) and is_genshi006(part, symbol)]
        if len(parts) == 1:
            if erci_judge(parts[0].args[0], symbol):
                a, b, c = Poly(parts[0].args[0].as_expr(), symbol).all_coeffs()
                if a.has(symbol) and a < 0:
                    f_minus = f - parts[0]
                    return f_minus.has(symbol) and yici_judge(f_minus, symbol)


# 得到函数的定义域
def get_cond(args):
    cond = S.Reals
    if len(args) == 2:
        cond = check_inter(args[1]) if args[1] else EmptySet()
    return cond


# 去绝对值
# 从给定区间(伪)随机取数,从而决定去绝对值后的正负号
def get_value_from_interval(interval):
    ll = interval.args[0]
    r = interval.args[1]
    if ll.is_finite and r.is_finite:
        return (ll + r) / 2
    elif ll.is_finite and not r.is_finite:
        return ll + 1
    elif not ll.is_finite and r.is_finite:
        return r - 1
    else:
        return 1


def delete_abs(f, interval=S.Reals):
    symbol = default_symbol(f)
    if isinstance(f, Abs) and "Abs" not in str(f.args[0]):  # 如：y=|f(x)|
        f_abs = f.args[0]
        lindian_jie1 = JieFangChen().solver(BaseEq([f_abs, S.Zero])).output[0].value
        lindian_jie = lindian_jie1[lindian_jie1.keys()[0]]
        sub_intervals = lindian_jie.complement(interval)
        interval_exp_pairs = []
        # 考虑到只有一个子区间的情况
        ll = sub_intervals.args[0]
        if ll.is_number:
            test_value = get_value_from_interval(sub_intervals)
            if f_abs.subs(symbol, test_value) > 0:
                interval_exp_pairs.append((sub_intervals, f, f_abs))
            else:
                interval_exp_pairs.append((sub_intervals, f, -f_abs))
        else:  # 多个子区间
            for sub in sub_intervals.args:
                test_value = get_value_from_interval(sub)
                if f_abs.subs(symbol, test_value) > 0:
                    interval_exp_pairs.append((sub, f_abs))
                else:
                    interval_exp_pairs.append((sub, -f_abs))
        return sub_intervals, interval_exp_pairs
    elif "Abs" in str(f):
        pairs = []
        intervals = interval
        for arg in f.args:
            if "Abs" in str(arg):
                sub_intervals, pair = delete_abs(arg, interval)
                pairs.append(pair)
                intervals = intervals.intersect(sub_intervals)
        expr_without_abs = []
        for sub_interval in intervals.args:
            f1 = f
            for pair in pairs:
                print(1)
                for sub_int in pair:
                    if sub_interval.is_subset(sub_int[0]):  # 是 sub_int[0] 的子集
                        f1 = f1.subs(sub_int[1], sub_int[2])
                        break

            expr_without_abs.append((sub_interval, f, f1))

        return intervals, expr_without_abs


def expr_split(expr):
    """
    将输入的expression按加减法拆分，以未知数为单位。e.g. expr_split(x+1/x) ==> [x,'+',1/x]
    :param expr: expression
    :return: list
    """
    output = []
    if isinstance(expr, Add):
        # f(x)+g(x)
        for arg in expr.args:
            if isinstance(arg, Mul):
                if arg.args[0] < 0:
                    output.append('-')
                    output.append((-1 * arg.args[0]) * arg.args[1])
                else:
                    output.append('+')
                    output.append(arg.args[0] * arg.args[1])
            else:
                if len(output) > 0:
                    output.append('+')
                output.append(arg)
    return output


def is_equal(expr1, expr2):
    """判断输入的两个expression是否相等，主要判断两个expr是power的情况"""
    if expr1 == expr2:
        return True
    if isinstance(expr1, Pow) and isinstance(expr2, Pow):
        time = simplify(expr1.args[1] / expr2.args[1])
        return expr1 ** time == expr2
    return False


def extract_eqs(eqlist_whole):
    """
    当未知数少于等式数目时，用root.jiefangchenzu解方程组会有问题，考虑将等式组进行抽取处理，
    确保每次抽取得到的每个等式组都含有全部未知数，然后将输出列表一次用root.jiefangchenzu进行求值，最后取每次结果的交集即可
    如输入BaseEqs([['3*a+b', '5'], ['3*b+a', '7'], ['a+b', '3']])，
    输出[BaseEqs([['3*a+b', '5'], ['3*b+a', '7']]),BaseEqs([['3*a+b', '5'], ['a+b', '3']]),BaseEqs([['3*b+a', '7'], ['a+b', '3']])]
    :param eqlist_whole: BaseEqs 其中含有的未知数少于等式数目
    :return: list(BaseEqs)
    """
    if not isinstance(eqlist_whole, BaseEqs):
        return None
    symbol_list = eqlist_whole.free_symbols()
    sym_list = set()
    for sym in symbol_list:
        sym_list.add(str(sym))
    output = list(combinations(eqlist_whole.value, len(sym_list)))
    result = []
    for i in range(len(output)):
        inter = set()
        for item in output[i]:
            inter |= (set(item[0] + item[1]).intersection(sym_list))
        if len(inter) == len(sym_list):
            result.append(BaseEqs(output[i]))
    return result


def solve_extracted_eqs(eqlist_extracted):
    """
    输入extract_eqs的结果，调用root.jiefangchenzu对各项求值，每项的交集就是最终结果
    :param eqlist_extracted: list(BaseEqs)
    :return:
    """
    result = []
    for i in range(len(eqlist_extracted)):
        jiede = JieFangChenZu().solver(eqlist_extracted[i]).output[0].value2pairs()
        if len(result) == 0:
            result = jiede
        else:
            result = [val for val in jiede if val in result]
    key = [k for k in result[0].keys()]
    value = []
    for item in result:
        value.append(tuple([item[k] for k in key]))
    # f_set=FiniteSet(value)
    return BaseSymbolValue({tuple(key): value})


if __name__ == '__main__':
    pass
